#include <iostream>
#include <string>
#include <sys/select.h>
#include <cmath>

#include "log.hpp"
#include "Socket.hpp"

using namespace Net_work;

const static int gbacklog = 8; // Tcp 在底层所能维护的全链接队列
const static int num = sizeof(fd_set) * 8;

class PollServer
{
    int _port;
    std::unique_ptr<Socket> _listensock;
    int _isrunning;

    Socket *_rfds_array[num];

public:
    PollServer(int port) : _port(port), _listensock(new TcpSocket()),_isrunning(false)
    {
    }

    void InitServer()
    {
        _listensock->BuildListenSocket(_port, gbacklog);
        for (int i = 0; i < num; i++)
        {
            _rfds_array[i] = nullptr;
        }
        _rfds_array[0] = _listensock.get(); // 得到listensock的指针
    }

    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < num; i++)
        {
            Socket *s = _rfds_array[i];

            if (s == nullptr)
                continue;

            if (_rfds_array[i]->GetSockFd() == _listensock->GetSockFd())
            {

                if (FD_ISSET(_listensock->GetSockFd(), &rfds)) // rfds 中是否存在 Listen 套接字
                {
                    // 说明 _listensock 中产生新的连接
                    lg.LogMessage(Info, "get a new link!\n");
                    std::string clientip;
                    uint16_t clientport;
                    Socket *sock = _listensock->AcceptConnection(&clientip, &clientport);
                    if (!sock)
                    {
                        lg.LogMessage(Error, "accept error!\n");
                        return;
                    }
                    lg.LogMessage(Info, "get a new link, ip:port -> %s:%d , sockfd: %d \n", clientip.c_str(), clientport, sock->GetSockFd());
                    // 将新的 fd(文件描述符) 添加进入sockfd_array
                    int pos = 0;
                    for (; pos < num; pos++)
                    {
                        if (_rfds_array[pos] == nullptr)
                        {
                            _rfds_array[pos] = sock;
                            break;
                        }
                    }

                    PrintDeBug();

                    if (pos == num)
                    {
                        // pos == num
                        // 说明服务器已经满载了
                        sock->CloseSocket();
                        delete sock;
                        lg.LogMessage(Warning, "server is full ...! \n");
                    }
                }
            }
            else
            {
                // 普通文件描述符 事件就绪

                // ?  这里数据读取是有问题的
                std::string buffer;
                bool res = _rfds_array[i]->Recv(&buffer, 1024);
                if (res)
                {
                    std::cout << "client say# " << buffer << std::endl;
                    buffer.clear();
                }
                else
                {
                    _rfds_array[i]->CloseSocket();
                    delete _rfds_array[i];
                    _rfds_array[i] = nullptr;
                }
            }
        }
    }

    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 这里不能直接accept ,如果没有数据,将会阻塞
            // ! 注意,这里所有的套接字都需要给 select
            // listensock 也需要交给 select

            fd_set rfds;
            FD_ZERO(&rfds); // 置为0

            // 记录最大的 sockfd
            int max_fd = _listensock->GetSockFd();
            for (int i = 0; i < num; i++)
            {
                if (_rfds_array[i] == nullptr)
                {
                    continue;
                }
                else
                {
                    int fd = _rfds_array[i]->GetSockFd();
                    FD_SET(fd, &rfds); // 将有文件描述符的都设置进 rfds
                    max_fd = std::max(max_fd, fd);
                }
            }

            struct timeval timeout = {5, 0};
            /*
             __time_t tv_sec;	 // 秒
            __suseconds_t tv_usec;	 // 微秒
            */

            //!         rfds 每一次都在更新
            int n = select(max_fd + 1, &rfds, nullptr, nullptr, &timeout);
            switch (n)
            {
            case 0:
                lg.LogMessage(Info, "select timeout , last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1:
                lg.LogMessage(Error, "select error!\n");
                break;

            default:
                // 就绪了,正常的fd
                lg.LogMessage(Info, "select success\n");
                HandlerEvent(rfds);
                break;
            }
        }
        _isrunning = false;
    }

    void PrintDeBug()
    {
        std::cout << "sockfd list: ";
        for (int i = 0; i < num; i++)
        {
            if (_rfds_array[i] == nullptr)
            {
                continue;
            }
            else
            {
                std::cout << _rfds_array[i]->GetSockFd() << " ";
            }
        }
        std::cout << std::endl;
    }
};
